package com.nirvana.config;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.intellij.ide.projectView.ProjectView;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.nirvana.MainPanel;
import com.nirvana.data.DependenceData;
import com.nirvana.data.DependenceMetaData;
import com.nirvana.factory.ItemFactory;
import com.nirvana.item.ResolvePropertyItem;
import com.nirvana.type.ItemType;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class PluginConfig {
    public static final String PLUGIN_GENERATE_FLAG = "// 代码由SRSC-Generator插件生成";
    public static Pattern collectionPattern = Pattern.compile("^(Array|List)\\[(.*)]$");
    public static Pattern tuplePattern = Pattern.compile("^\\(.*\\)$");
    private static final Pattern attrPattern = Pattern.compile("\\bval\\s+(\\w+)\\s*=\\s*.*?\\(\\s*(\\d+)\\s*\\)");
    private static final String CONFIG_PATH = "generate-plugin-config.json";
    private static final String[] DEFAULT_TYPES = new String[]{
            "String",
            "Int",
            "Float",
            "Double",
            "Long",
            "Map",
            "List"
    };
    private static final String[] DEFAULT_RESOLVES = new String[]{
            "auto"
    };
    private static Project PROJECT;
    private static PluginConfig INSTANCE;
    private static String[] types;
    private static Map<String, String[]> resolveMap;

    public static MainPanel mainPanel;

    private String flatBuffersPackage = "mtj_common/src/main/scala/com/nirvana/bean/flatBuffers";
    private String defPackage = "mtj_common/src/main/scala/com/nirvana/bean/xmlBean";
    private String convertUtilPackage = "com.nirvana.util.ConvertUtil";
    private String driver = "com.mysql.cj.jdbc.Driver";
    private String url = "jdbc:mysql://localhost:3306/mtj_1123";
    private String user = "root";
    private String password = "123456";

    private String entityPackage = "com.nirvana.data.entity";
    private String daoPackage = "com.nirvana.data.dao";
    private String daoImplPackage = "com.nirvana.data.dao.impl";
    private String factoryPackage = "com.nirvana.data.factory";
    private String businessPackage = "com.nirvana.data.bussiness";
    private List<DependenceData> dependenceDataList = new ArrayList<>();
    private Map<String/* simpleName */, DependenceData> dependenceDataMap = new HashMap<>();

    private Set<String> ignoreFields = new HashSet<>();
    private static Set<String> ignoreAttrFields = new HashSet<>();

    private PluginConfig() {
    }

    public static PluginConfig getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new PluginConfig();
        }
        return INSTANCE;
    }

    public static void init(Project _project) {
        PROJECT = _project;
        reload();
    }

    private static void reload() {
        initIgnoreAttr();
        Path path = Paths.get(getProjectBasePath() + "/" + CONFIG_PATH);
        if (Files.notExists(path)) {
            try {
                Files.createFile(path);
                reloadAllFilesFromDisk();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        try {
            byte[] bytes = Files.readAllBytes(path);
            PluginConfig pluginConfig = JSON.parseObject(bytes, PluginConfig.class);
            if (INSTANCE == null) {
                if (pluginConfig == null) {
                    INSTANCE = new PluginConfig();
                    return;
                } else {
                    INSTANCE = pluginConfig;
                }
            } else {
                if (pluginConfig == null) return;
                INSTANCE.copy(pluginConfig);
            }

            INSTANCE.dependenceDataList.forEach(data -> INSTANCE.dependenceDataMap.put(data.simpleName(), data));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static void initIgnoreAttr() {
        Path path = Paths.get(getProjectBasePath() + "/mtj_common/src/main/scala/com/nirvana/bean/aType.scala");
        if (Files.notExists(path)) return;
        ignoreAttrFields = new HashSet<>();
        try {
            List<String> lines = Files.readAllLines(path);
            for (String line : lines) {
                Matcher matcher = attrPattern.matcher(line);
                if (matcher.find()) ignoreAttrFields.add(matcher.group(1));
            }
        } catch (IOException e) {
            Messages.showErrorDialog(e.getMessage(), "错误提示");
        }
    }

    private void copy(PluginConfig other) {
        flatBuffersPackage = other.flatBuffersPackage;
        defPackage = other.defPackage;
        convertUtilPackage = other.convertUtilPackage;
        driver = other.driver;
/*
        url = other.url;
        user = other.user;
        password = other.password;
*/
        entityPackage = other.entityPackage;
        daoPackage = other.daoPackage;
        daoImplPackage = other.daoImplPackage;
        factoryPackage = other.factoryPackage;
        businessPackage = other.businessPackage;
        dependenceDataList = other.dependenceDataList;
        ignoreFields = other.ignoreFields;
    }

    public void reloadPlugin() {
        types = null;
        resolveMap = null;
        reloadAllFilesFromDisk();
        reload();
        populateConfig();
        ItemFactory.getCommonItem(ItemType.DefPropertyItem).refreshAllDefPropertyItemComBox();
    }

    public void afterInit(MainPanel _mainPanel) {
        mainPanel = _mainPanel;
        populateConfig();
    }

    private void populateConfig() {
        mainPanel.dbDriverTextField.setText(driver);
        // context.dbUrlTextField.setText(url);
        // context.dbUserTextField.setText(user);
        // context.dbPasswordTextField.setText(password);
        mainPanel.entityPackageTextField.setText(entityPackage);
        mainPanel.daoPackageTextField.setText(daoPackage);
        mainPanel.daoImplPackageTextField.setText(daoImplPackage);
        mainPanel.factoryPackageTextField.setText(factoryPackage);
        mainPanel.businessPackageTextField.setText(businessPackage);
        mainPanel.xmlFilePathTextField.setText(projectXmlPath());

        ItemFactory.getCommonItem(ItemType.ResolvePropertyItem).populateData(dependenceDataList);
    }

    public static String getProjectBasePath() {
        return PROJECT.getBasePath();
    }

    private String projectXmlPath() {
        return getProjectBasePath() + "/mtj_config/xml";
    }

    public String defBasePath() {
        return getProjectBasePath() + "/" + defPackage;
    }

    public String flatBuffersBasePath() {
        return getProjectBasePath() + "/" + flatBuffersPackage;
    }

    public void saveConfig() {
        refreshConfig();
        String jsonString = JSONObject.toJSONString(this, JSONWriter.Feature.PrettyFormat);
        try {
            Files.writeString(Paths.get(getProjectBasePath() + "/" + CONFIG_PATH), jsonString);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void refreshConfig() {
        driver = mainPanel.dbDriverTextField.getText();
        url = mainPanel.dbUrlTextField.getText();
        user = mainPanel.dbUserTextField.getText();
        password = mainPanel.dbPasswordTextField.getText();
        entityPackage = mainPanel.entityPackageTextField.getText();
        daoPackage = mainPanel.daoPackageTextField.getText();
        daoImplPackage = mainPanel.daoImplPackageTextField.getText();
        factoryPackage = mainPanel.factoryPackageTextField.getText();
        businessPackage = mainPanel.businessPackageTextField.getText();
    }

    public void clearCache() {
        dependenceDataList = new ArrayList<>();
        dependenceDataMap = new HashMap<>();
        types = null;
        resolveMap = null;
    }

    public int addNewDependence(ResolvePropertyItem item, int index) {
        String propertyName = item.getPropertyName();
        String resolveMethod = item.getResolveMethod();
        if (StringUtils.isBlank(propertyName) || StringUtils.isBlank(resolveMethod)) return index;

        String simpleName = simpleName(propertyName);
        DependenceData data = dependenceDataMap.getOrDefault(simpleName, null);
        if (data == null) {
            data = new DependenceData(propertyName);
            data.setIndex(index++);
            dependenceDataList.add(data);
            dependenceDataMap.put(simpleName, data);
        }
        data.addNewMetaData(resolveMethod, item.getParamTypeIndex(), item.getShow(), item.getToolTip());
        return index;
    }

    public int getTypeIndex(String type) {
        String[] typeArray = types();
        for (int i = 0; i < typeArray.length; i++) {
            if (typeArray[i].equalsIgnoreCase(type)) return i;
        }
        return 0;
    }

    public int getResolveIndex(String type, String resolveMethod) {
        if (type == null || resolveMethod == null) return 0;
        DependenceData data = dependenceDataMap.getOrDefault(type, null);
        if (data == null) return 0;
        return data.resolveIndex(resolveMethod);
    }

    public String[] types() {
        if (dependenceDataList.isEmpty()) return DEFAULT_TYPES;
        if (types == null) {
            types = dependenceDataList.stream().filter(data -> data.getShow() == 1).sorted(new Comparator<>() {
                @Override
                public int compare(DependenceData o1, DependenceData o2) {
                    return o1.getIndex() - o2.getIndex();
                }

                @Override
                public boolean equals(Object obj) {
                    return false;
                }
            }).map(DependenceData::simpleName).toArray(String[]::new);
        }
        return types;
    }

    public String[] resolves(String type) {
        if (dependenceDataList.isEmpty()) return DEFAULT_RESOLVES;
        if (resolveMap == null) {
            resolveMap = new HashMap<>();
        }
        if (! resolveMap.containsKey(type)) {
            DependenceData data;
            data = dependenceDataMap.getOrDefault(type, null);
            String[] resolves = data.getMetaDataList().stream().map(DependenceMetaData::getResolveMethod).toArray(String[]::new);
            resolveMap.put(type, resolves);
        }
        return resolveMap.get(type);
    }

    public static void reloadAllFilesFromDisk() {
        ApplicationManager.getApplication().invokeLater(() -> {
            VirtualFileManager.getInstance().syncRefresh();
            ProjectView.getInstance(PROJECT).refresh();
        });
    }

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public String url() {
        return mainPanel.ormDbUrlTextField.getText();
    }

    public String dbUrl() {
        return url().substring(0, url().lastIndexOf("/"));
    }

    public String dbName() {
        return url().substring(url().lastIndexOf("/") + 1);
    }

/*
    public void setUrl(String url) {
        this.url = url;
    }
*/

    public String user() {
        return user;
    }

/*
    public void setUser(String user) {
        this.user = user;
    }
*/

    public String password() {
        return password;
    }

/*
    public void setPassword(String password) {
        this.password = password;
    }
*/

    public String getEntityPackage() {
        return entityPackage;
    }

    public void setEntityPackage(String entityPackage) {
        this.entityPackage = entityPackage;
    }

    public String getDaoPackage() {
        return daoPackage;
    }

    public void setDaoPackage(String daoPackage) {
        this.daoPackage = daoPackage;
    }

    public String getDaoImplPackage() {
        return daoImplPackage;
    }

    public void setDaoImplPackage(String daoImplPackage) {
        this.daoImplPackage = daoImplPackage;
    }

    public String getFactoryPackage() {
        return factoryPackage;
    }

    public void setFactoryPackage(String factoryPackage) {
        this.factoryPackage = factoryPackage;
    }

    public String getBusinessPackage() {
        return businessPackage;
    }

    public void setBusinessPackage(String businessPackage) {
        this.businessPackage = businessPackage;
    }

    public List<DependenceData> getDependenceDataList() {
        return dependenceDataList;
    }

    public void setDependenceDataList(List<DependenceData> dependenceDataList) {
        this.dependenceDataList = dependenceDataList;
    }

    public DependenceData getDependenceDataByType(String type) {
        return dependenceDataMap.getOrDefault(type, null);
    }

    public String getDefPackage() {
        return defPackage;
    }

    public void setDefPackage(String defPackage) {
        this.defPackage = defPackage;
    }

    public String getConvertUtilPackage() {
        return convertUtilPackage;
    }

    public void setConvertUtilPackage(String convertUtilPackage) {
        this.convertUtilPackage = convertUtilPackage;
    }

    public Boolean checkIgnoreField(String fieldName) {
        return ignoreFields.contains(fieldName) || ignoreAttrFields.contains(fieldName);
    }

    public Boolean isAttrName(String name) {
        return ignoreAttrFields.contains(name);
    }

    public Set<String> getIgnoreFields() {
        return ignoreFields;
    }

    public void setIgnoreFields(Set<String> ignoreFields) {
        this.ignoreFields = ignoreFields;
    }

    public static String simpleName(String fullClassName) {
        return ! fullClassName.contains(".") ? fullClassName : fullClassName.substring(fullClassName.lastIndexOf(".") + 1);
    }

    public String defDotPackage() {
        return defPackage.substring(defPackage.indexOf("com/nirvana")).replaceAll("/", ".");
    }

    public String flatBuffersDotPackage() {
        return flatBuffersPackage.substring(flatBuffersPackage.indexOf("com/nirvana")).replaceAll("/", ".");
    }

    public String entityPath() {
        return convertPackageToPath(entityPackage);
    }

    public String daoPath() {
        return convertPackageToPath(daoPackage);
    }

    public String daoImplPath() {
        return convertPackageToPath(daoImplPackage);
    }

    public String factoryPath() {
        return convertPackageToPath(factoryPackage);
    }

    public String businessPath() {
        return convertPackageToPath(businessPackage);
    }

    public String fbsBasePath() {
        return flatBuffersBasePath() + "/fbs";
    }

    public boolean useFlatBuffers() {
        return mainPanel.useFlatBuffersCheckBox.isSelected();
    }

    private String convertPackageToPath(String _package) {
        return getProjectBasePath() + "/mtj_game/src/main/java/" + _package.replaceAll("\\.", "/");
    }

    public void addFilterField(String content) {
        ignoreFields = Arrays.stream(content.replaceAll("[^a-zA-Z0-9_]", " ").split("\\s+")).filter(StringUtils::isNotBlank).collect(Collectors.toSet());
    }

    public String flatBuffersInstallPath() {
        return mainPanel.flatBuffersInstallPathTextField.getText();
    }

    public boolean reGenerateCommon() {
        return mainPanel.rebuildCommonFbsCheckBox.isSelected();
    }

    public boolean skipGenerateDef() {
        return mainPanel.skipDefGenerateCheckBox.isSelected();
    }

    public boolean extendsGroupDef() {
        return mainPanel.extendsGroupDefDefCheckBox.isSelected();
    }

    public void extendsGroupDef(boolean extend) {
        mainPanel.extendsGroupDefDefCheckBox.setSelected(extend);
    }
}